//
//  ZMRoute.swift
//  Reader17kSDK
//
//  Created by Ink on 2020/11/9.
//

import UIKit

public typealias UrlRouteHandleOver = ((_ url: String,
                                        _ routeParsing: ZMRouteParsing?,
                                        _ params: [String : Any]?,
                                        _ animation: ZMRoute.Animation,
                                        _ from: UIViewController?,
                                        _ completion: ZMValueHandler?) -> Void)

public typealias ZMRouteParsing = (type: UrlRouteProtocol.Type?, queryParams: [String : Any?], captures: [String]?)

public protocol UrlRouteProtocol where Self: UIViewController {
    
    /// 路由 正则
    static var routeRegexTarget: String { get }
    
    /// Constructor
    ///
    /// - Parameters:
    ///   - params: queryParams + customParams
    ///   - captures: pathParams
    static func create(params: [String : Any]?, captures: [String]?) -> Self?
}

extension UrlRouteProtocol {
    
    public static func openRoute() { routeRegexTarget.openRoute() }
    
    public static func start() { openRoute() }
}

public class ZMRoute {
    
    public static let shared = ZMRoute()
    
    public var urlRouteSore = [UrlRouteProtocol.Type]()
    
    public var urlRouteHandleOver:UrlRouteHandleOver?
}

public extension ZMRoute {
    enum Animation {
        case push(animated: Bool)
        case present(animated: Bool)
        case auto
    }
}

public extension ZMRoute {
    
    class func open(url: String, 
                    params: [String : Any?]? = nil,
                    animation: ZMRoute.Animation = .auto,
                    from: UIViewController? = nil,
                    completion: ZMValueHandler? = nil) {
        let routeParsing = routeParsing(url: url)
        let tempParams = [String : Any?]().merging(routeParsing?.queryParams).merging(params).compactMapValues { $0 }
        shared.urlRouteHandleOver?(url,routeParsing,tempParams,animation,from,completion)
    }
    
    /// 安全地展示一个视图控制器
    class func safePresent(_ viewControllerToPresent: UIViewController?,
                                  from fromViewController: UIViewController?,
                                  animated flag: Bool,
                                  completion: (() -> Void)? = nil) {
        guard let viewControllerToPresent = viewControllerToPresent,
              let fromViewController = fromViewController else {
            assertionFailure("viewControllerToPresent or fromViewController is nil.")
            return
        }
        
        guard fromViewController.presentedViewController == nil else {
            assertionFailure("fromViewController is already presenting another view controller.")
            return
        }
        
        guard fromViewController.isBeingPresented == false,
              fromViewController.isBeingDismissed == false,
              viewControllerToPresent.isBeingPresented == false,
              viewControllerToPresent.isBeingDismissed == false else {
            assertionFailure("Either fromViewController or viewControllerToPresent is in an invalid state.")
            return
        }
        
        fromViewController.present(viewControllerToPresent, animated: flag, completion: completion)
    }
}

public extension ZMRoute {
    
    /// 注册 modules
    class func register(@ResultBuilder<UrlRouteProtocol.Type> _ makeTypes: () -> [UrlRouteProtocol.Type]) {
        makeTypes().forEach { register(type:$0) }
    }
    
    /// 注册某个 module
    class func register(type: UrlRouteProtocol.Type) {
        remove(type)
        shared.urlRouteSore.append(type)
    }
    
    /// 注销某个 module
    class func remove(_ type:UrlRouteProtocol.Type) {
        shared.urlRouteSore = shared.urlRouteSore.filter({$0 != type})
    }
    
    /// 注销所有的 key 对应的 module
    class func removeAll() {
        shared.urlRouteSore.removeAll()
    }
    
    /// 校验url对应的 path 是否被注册
    class func routeRegexType(url:String) -> UrlRouteProtocol.Type? {
        shared.urlRouteSore.first {  url.routePath?.regexCaptures(pattern: $0.routeRegexTarget.routePath)?.result == true }
    }
    
    class func routeParsing(url: String) -> (type: UrlRouteProtocol.Type?, queryParams: [String : Any?], captures: [String]?)? {
        
        var queryParams = [String : Any?]()
        let type = routeRegexType(url: url)
        let captures = url.routePath?.regexCaptures(pattern: type?.routeRegexTarget.routePath)?.captures
        queryParams.merge(url.queryDictionary ?? [:])
        return (type, queryParams, captures)
    }
}

public extension URL {
    
    var components: URLComponents? {
        URLComponents(url: self, resolvingAgainstBaseURL: true)
    }
    
    var queryDictionary: [String: Any]? {
        return queryJSONObject as? [String: Any]
    }
    
    var queryJsonString: String? {
        guard let query = self.query else { return nil }
        
        var dict: [String: Any] = [:]
        query.components(separatedBy: "&").forEach { pair in
            let parts = pair.components(separatedBy: "=")
            guard let key = parts.first?.removingPercentEncoding else { return }
            let value = parts.count > 1 ? parts[1].removingPercentEncoding ?? "" : ""
            dict[key] = value
        }
        
        guard let data = try? JSONSerialization.data(withJSONObject: dict, options: []) else {
            return nil
        }
        return String(data: data, encoding: .utf8)
    }
    
    var queryJSONObject: Any? {
        guard let data = queryJsonString?.data(using: .utf8) else { return nil }
        return try? JSONSerialization.jsonObject(with: data, options: [])
    }
}

public extension String {
    
    var url: URL? { URL(string: self) }
    
    var routePath: String? { components(separatedBy: "?").first }
    
    func openRoute(params: [String : Any?]? = nil,
                          conduct: ((_ url: String) -> String)? = nil,
                          animation: ZMRoute.Animation = .auto,
                          from: UIViewController? = nil,
                          completion: ZMValueHandler? = nil) {
        let url = conduct?(self) ?? self
        ZMRoute.open(url: url,
                     params: params,
                     animation: animation,
                     from: from,
                     completion: completion)
         
    }
    
    func merge(params: [String : Any?]?,urlEncode: Bool = true) -> Self {
        var str = self
        if let paramsStr = params?.compactMapValues({ $0 }).transitionString(urlEncode: urlEncode) {
            str += str.contains("?") ? "&" : "?"
            str += paramsStr
        }
        return str
    }
    
    var queryDictionary: [String : Any]? { url?.queryDictionary }
    /// pathParams
    func regexCaptures(pattern: String?) -> (result: Bool, captures: [String])? {
        guard let pattern = pattern,
              let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            return nil
        }
        var captures = [String]()
        let matches = regex.matches(in: self, options: [], range: NSRange(startIndex..., in: self))
        matches.forEach {
            for idx in 0..<$0.numberOfRanges {
                captures.append((self as NSString).substring(with: $0.range(at: idx)))
            }
        }
        return (matches.count > 0, captures)
    }
    
    /// 多次解码字符串，直到 URL 的 `host` 不再编码
    var decodeUntilHost: String {
        var currentString = self
        if let url = URL(string: currentString), url.host != nil {
            return currentString
        }

        // 不断尝试解码，直到 host 不再被编码
        while let decodedString = currentString.removingPercentEncoding,
              decodedString != currentString,
              let decodedURL = URL(string: decodedString),
              decodedURL.host != nil {
            currentString = decodedString
        }

        // 返回最终解码后的字符串
        return currentString
    }
}
